home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / diff / dist / diff.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-02-19  |  16.3 KB  |  656 lines

  1. /* GNU DIFF main routine.
  2.    Copyright (C) 1988, 1989 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU DIFF is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU DIFF; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* GNU DIFF was written by Mike Haertel, David Hayes,
  21.    Richard Stallman and Len Tower.  */
  22.  
  23. #define GDIFF_MAIN
  24. #include "regex.h"
  25. #include "diff.h"
  26. #include "getopt.h"
  27.  
  28.  
  29. /* Nonzero for -r: if comparing two directories,
  30.    compare their common subdirectories recursively.  */
  31.  
  32. int recursive;
  33.  
  34. /* For debugging: don't do discard_confusing_lines.  */
  35.  
  36. int no_discards;
  37.  
  38. /* Return a string containing the command options with which diff was invoked.
  39.    Spaces appear between what were separate ARGV-elements.
  40.    There is a space at the beginning but none at the end.
  41.    If there were no options, the result is an empty string.
  42.  
  43.    Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
  44.    the length of that vector.  */
  45.  
  46. static char *
  47. option_list (optionvec, count)
  48.      char **optionvec;  /* Was `vector', but that collides on Alliant.  */
  49.      int count;
  50. {
  51.   int i;
  52.   int length = 0;
  53.   char *result;
  54.  
  55.   for (i = 0; i < count; i++)
  56.     length += strlen (optionvec[i]) + 1;
  57.  
  58.   result = (char *) xmalloc (length + 1);
  59.   result[0] = 0;
  60.  
  61.   for (i = 0; i < count; i++)
  62.     {
  63.       strcat (result, " ");
  64.       strcat (result, optionvec[i]);
  65.     }
  66.  
  67.   return result;
  68. }
  69.  
  70. static struct option longopts[] =
  71. {
  72.   {"ignore-blank-lines", 0, 0, 'B'},
  73.   {"context", 2, 0, 129},
  74.   {"ifdef", 1, 0, 'D'},
  75.   {"show-function-line", 1, 0, 'F'},
  76.   {"speed-large-files", 0, 0, 'H'},
  77.   {"ignore-matching-lines", 1, 0, 'I'},
  78.   {"entire-new-file", 0, 0, 'N'},
  79.   {"starting-file", 1, 0, 'S'},
  80.   {"initial-tab", 0, 0, 'T'},
  81.   {"text", 0, 0, 'a'},
  82.   {"all-text", 0, 0, 'a'},
  83.   {"ascii", 0, 0, 'a'},
  84.   {"ignore-space-change", 0, 0, 'b'},
  85.   {"minimal", 0, 0, 'd'},
  86.   {"ed", 0, 0, 'e'},
  87.   {"reversed-ed", 0, 0, 'f'},
  88.   {"ignore-case", 0, 0, 'i'},
  89.   {"print", 0, 0, 'l'},
  90.   {"rcs", 0, 0, 'n'},
  91.   {"show-c-function", 0, 0, 'p'},
  92.   {"binary", 0, 0, 'q'},
  93.   {"brief", 0, 0, 'q'},
  94.   {"recursive", 0, 0, 'r'},
  95.   {"report-identical-files", 0, 0, 's'},
  96.   {"expand-tabs", 0, 0, 't'},
  97.   {"ignore-all-space", 0, 0, 'w'},
  98.   {"version", 0, 0, 'v'},
  99.   {0, 0, 0, 0}
  100. };
  101.  
  102. main (argc, argv)
  103.      int argc;
  104.      char *argv[];
  105. {
  106.   int val;
  107.   int c;
  108.   int prev = -1;
  109.   int longind;
  110.   extern char *version_string;
  111.  
  112.   program = argv[0];
  113.  
  114.   /* Do our initializations. */
  115.   output_style = OUTPUT_NORMAL;
  116.   always_text_flag = FALSE;
  117.   ignore_space_change_flag = FALSE;
  118.   ignore_all_space_flag = FALSE;
  119.   length_varies = FALSE;
  120.   ignore_case_flag = FALSE;
  121.   ignore_blank_lines_flag = FALSE;
  122.   ignore_regexp = 0;
  123.   function_regexp = 0;
  124.   print_file_same_flag = FALSE;
  125.   entire_new_file_flag = FALSE;
  126.   no_details_flag = FALSE;
  127.   context = -1;
  128.   line_end_char = '\n';
  129.   tab_align_flag = FALSE;
  130.   tab_expand_flag = FALSE;
  131.   recursive = FALSE;
  132.   paginate_flag = FALSE;
  133.   ifdef_string = NULL;
  134.   heuristic = FALSE;
  135.   dir_start_file = NULL;
  136.   msg_chain = NULL;
  137.   msg_chain_end = NULL;
  138.   no_discards = 0;
  139.  
  140.   /* Decode the options.  */
  141.  
  142.   while ((c = getopt_long (argc, argv,
  143.                "0123456789abBcC:dD:efF:hHiI:lnNpqrsS:tTvw",
  144.                longopts, &longind)) != EOF)
  145.     {
  146.       if (c == 0)        /* Long option. */
  147.     c = longopts[longind].val;
  148.       switch (c)
  149.     {
  150.       /* All digits combine in decimal to specify the context-size.  */
  151.     case '1':
  152.     case '2':
  153.     case '3':
  154.     case '4':
  155.     case '5':
  156.     case '6':
  157.     case '7':
  158.     case '8':
  159.     case '9':
  160.     case '0':
  161.       if (context == -1)
  162.         context = 0;
  163.       /* If a context length has already been specified,
  164.          more digits allowed only if they follow right after the others.
  165.          Reject two separate runs of digits, or digits after -C.  */
  166.       else if (prev < '0' || prev > '9')
  167.         fatal ("context length specified twice");
  168.  
  169.       context = context * 10 + c - '0';
  170.       break;
  171.  
  172.     case 'a':
  173.       /* Treat all files as text files; never treat as binary.  */
  174.       always_text_flag = 1;
  175.       break;
  176.  
  177.     case 'b':
  178.       /* Ignore changes in amount of whitespace.  */
  179.       ignore_space_change_flag = 1;
  180.       length_varies = 1;
  181.       break;
  182.  
  183.     case 'B':
  184.       /* Ignore changes affecting only blank lines.  */
  185.       ignore_blank_lines_flag = 1;
  186.       break;
  187.  
  188.     case 'C':
  189.     case 129:        /* +context[=lines] */
  190.       if (optarg)
  191.         {
  192.           if (context >= 0)
  193.         fatal ("context length specified twice");
  194.           {
  195.         char *p;
  196.         for (p = optarg; *p; p++)
  197.           if (*p < '0' || *p > '9')
  198.             fatal ("invalid context length argument");
  199.           }
  200.           context = atoi (optarg);
  201.         }
  202.  
  203.       /* Falls through.  */
  204.     case 'c':
  205.       /* Make context-style output.  */
  206.       specify_style (OUTPUT_CONTEXT);
  207.       break;
  208.  
  209.     case 'd':
  210.       /* Don't discard lines.  This makes things slower (sometimes much
  211.          slower) but will find a guaranteed minimal set of changes.  */
  212.       no_discards = 1;
  213.       break;
  214.  
  215.     case 'D':
  216.       /* Make merged #ifdef output.  */
  217.       specify_style (OUTPUT_IFDEF);
  218.       ifdef_string = optarg;
  219.       break;
  220.  
  221.     case 'e':
  222.       /* Make output that is a valid `ed' script.  */
  223.       specify_style (OUTPUT_ED);
  224.       break;
  225.  
  226.     case 'f':
  227.       /* Make output that looks vaguely like an `ed' script
  228.          but has changes in the order they appear in the file.  */
  229.       specify_style (OUTPUT_FORWARD_ED);
  230.       break;
  231.  
  232.     case 'F':
  233.       /* Show, for each set of changes, the previous line that
  234.          matches the specified regexp.  Currently affects only
  235.          context-style output.  */
  236.       function_regexp = optarg;
  237.       break;
  238.  
  239.     case 'h':
  240.       /* Split the files into chunks of around 1500 lines
  241.          for faster processing.  Usually does not change the result.
  242.  
  243.          This currently has no effect.  */
  244.       break;
  245.  
  246.     case 'H':
  247.       /* Turn on heuristics that speed processing of large files
  248.          with a small density of changes.  */
  249.       heuristic = 1;
  250.       break;
  251.  
  252.     case 'i':
  253.       /* Ignore changes in case.  */
  254.       ignore_case_flag = 1;
  255.       break;
  256.  
  257.     case 'I':
  258.       /* Ignore changes affecting only lines that match the
  259.          specified regexp.  */
  260.       ignore_regexp = optarg;
  261.       break;
  262.  
  263.     case 'l':
  264.       /* Pass the output through `pr' to paginate it.  */
  265.       paginate_flag = 1;
  266.       break;
  267.  
  268.     case 'n':
  269.       /* Output RCS-style diffs, like `-f' except that each command
  270.          specifies the number of lines affected.  */
  271.       specify_style (OUTPUT_RCS);
  272.       break;
  273.  
  274.     case 'N':
  275.       /* When comparing directories, if a file appears only in one
  276.          directory, treat it as present but empty in the other.  */
  277.       entire_new_file_flag = 1;
  278.       break;
  279.  
  280.     case 'p':
  281.       /* Make context-style output and show name of last C function.  */
  282.       specify_style (OUTPUT_CONTEXT);
  283.       function_regexp = "^[_a-zA-Z]";
  284.       break;
  285.  
  286.     case 'q':
  287.       no_details_flag = 1;
  288.       break;
  289.  
  290.     case 'r':
  291.       /* When comparing directories, 
  292.          recursively compare any subdirectories found.  */
  293.       recursive = 1;
  294.       break;
  295.  
  296.     case 's':
  297.       /* Print a message if the files are the same.  */
  298.       print_file_same_flag = 1;
  299.       break;
  300.  
  301.     case 'S':
  302.       /* When comparing directories, start with the specified
  303.          file name.  This is used for resuming an aborted comparison.  */
  304.       dir_start_file = optarg;
  305.       break;
  306.  
  307.     case 't':
  308.       /* Expand tabs to spaces in the output so that it preserves
  309.          the alignment of the input files.  */
  310.       tab_expand_flag = 1;
  311.       break;
  312.  
  313.     case 'T':
  314.       /* Use a tab in the output, rather than a space, before the
  315.          text of an input line, so as to keep the proper alignment
  316.          in the input line without changing the characters in it.  */
  317.       tab_align_flag = 1;
  318.       break;
  319.  
  320.     case 'v':
  321.       printf ("GNU diff version %s\n", version_string);
  322.       break;
  323.  
  324.     case 'w':
  325.       /* Ignore horizontal whitespace when comparing lines.  */
  326.       ignore_all_space_flag = 1;
  327.       length_varies = 1;
  328.       break;
  329.  
  330.     default:
  331.       usage ();
  332.     }
  333.       prev = c;
  334.     }
  335.  
  336.   if (optind != argc - 2)
  337.     usage ();
  338.  
  339.   if (ignore_regexp)
  340.     {
  341.       char *val;
  342.       bzero (&ignore_regexp_compiled, sizeof ignore_regexp_compiled);
  343.       val = re_compile_pattern (ignore_regexp, strlen (ignore_regexp),
  344.                 &ignore_regexp_compiled);
  345.       if (val != 0)
  346.     error ("regexp to ignore: ", val);
  347.       ignore_regexp_compiled.fastmap = (char *) xmalloc (256);
  348.     }
  349.  
  350.   if (function_regexp)
  351.     {
  352.       char *val;
  353.       bzero (&function_regexp_compiled, sizeof function_regexp_compiled);
  354.       val = re_compile_pattern (function_regexp, strlen (function_regexp),
  355.                 &function_regexp_compiled);
  356.       if (val != 0)
  357.     error ("regexp for previous line: ", val);
  358.       function_regexp_compiled.fastmap = (char *) xmalloc (256);
  359.     }
  360.  
  361.   if (output_style != OUTPUT_CONTEXT)
  362.     context = 0;
  363.   else if (context == -1)
  364.     /* Default amount of context for -c.  */
  365.     context = 3;
  366.  
  367.   switch_string = option_list (argv + 1, optind - 1);
  368.  
  369.   val = compare_files (0, argv[optind], 0, argv[optind + 1], 0);
  370.  
  371.   /* Print any messages that were saved up for last.  */
  372.   print_message_queue ();
  373.  
  374.   exit (val);
  375. }
  376.  
  377. usage ()
  378. {
  379.   fprintf (stderr, "\
  380. Usage: diff [-#] [-abBcdefhHilnNprstTvw] [-C lines] [-F regexp] [-I regexp]\n\
  381.        [-S file] [-D symbol] [+ignore-blank-lines] [+context[=lines]]\n\
  382.        [+ifdef symbol] [+show-function-line regexp] [+speed-large-files]\n");
  383.   fprintf (stderr, "\
  384.        [+ignore-matching-lines regexp] [+entire-new-file] [+initial-tab]\n\
  385.        [+starting-file file] [+text] [+all-text] [+ascii] [+minimal]\n\
  386.        [+ignore-space-change] [+ed] [+reversed-ed] [+ignore-case] [+print]\n");
  387.   fprintf (stderr, "\
  388.        [+rcs] [+show-c-function] [+binary] [+brief] [+recursive]\n\
  389.        [+report-identical-files] [+expand-tabs] [+ignore-all-space]\n\
  390.        [+version] path1 path2\n");
  391.   exit (1);
  392.  
  393. specify_style (style)
  394.      enum output_style style;
  395. {
  396.   if (output_style != OUTPUT_NORMAL
  397.       && output_style != style)
  398.     error ("conflicting specifications of output style");
  399.   output_style = style;
  400. }
  401.  
  402. /* Compare two files (or dirs) with specified names
  403.    DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion.
  404.    (if DIR0 is 0, then the name is just NAME0, etc.)
  405.    This is self-contained; it opens the files and closes them.
  406.  
  407.    Value is 0 if files are identical, 1 if different,
  408.    2 if there is a problem opening them.  */
  409.  
  410. int
  411. compare_files (dir0, name0, dir1, name1, depth)
  412.      char *dir0, *dir1;
  413.      char *name0, *name1;
  414.      int depth;
  415. {
  416.   struct file_data inf[2];
  417.   register int i;
  418.   int val;
  419.   int errorcount = 0;
  420.   int stat_result[2];
  421.  
  422.   /* If this is directory comparison, perhaps we have a file
  423.      that exists only in one of the directories.
  424.      If so, just print a message to that effect.  */
  425.  
  426.   if (! entire_new_file_flag && (name0 == 0 || name1 == 0))
  427.     {
  428.       char *name = name0 == 0 ? name1 : name0;
  429.       char *dir = name0 == 0 ? dir1 : dir0;
  430.       message ("Only in %s: %s\n", dir, name);
  431.       /* Return 1 so that diff_dirs will return 1 ("some files differ").  */
  432.       return 1;
  433.     }
  434.  
  435.   /* Mark any nonexistent file with -1 in the desc field.  */
  436.  
  437.   inf[0].desc = name0 == 0 ? -1 : 0;
  438.   inf[1].desc = name1 == 0 ? -1 : 0;
  439.  
  440.   /* Now record the full name of each file, including nonexistent ones.  */
  441.  
  442.   if (name0 == 0)
  443.     name0 = name1;
  444.   if (name1 == 0)
  445.     name1 = name0;
  446.  
  447.   inf[0].name = dir0 == 0 ? name0 : concat (dir0, "/", name0);
  448.   inf[1].name = dir1 == 0 ? name1 : concat (dir1, "/", name1);
  449.  
  450.   /* Stat the files.  Record whether they are directories.
  451.      Record in stat_result whether stat fails.  */
  452.  
  453.   for (i = 0; i <= 1; i++)
  454.     {
  455.       bzero (&inf[i].stat, sizeof(struct stat));
  456.       inf[i].dir_p = 0;
  457.       stat_result[i] = 0;
  458.  
  459.       if (inf[i].desc != -1
  460.       && strcmp (inf[i].name, "-"))
  461.     {
  462.       char *filename = inf[i].name;
  463.  
  464.       stat_result[i] = stat (filename, &inf[i].stat);
  465.       if (stat_result[i] < 0)
  466.         {
  467.           perror_with_name (filename);
  468.           errorcount = 1;
  469.         }
  470.       else
  471.         inf[i].dir_p = (S_IFDIR == (inf[i].stat.st_mode & S_IFMT));
  472.     }
  473.     }
  474.  
  475.   /* See if the two named files are actually the same physical file.
  476.      If so, we know they are identical without actually reading them.  */
  477.  
  478.   if (inf[0].stat.st_ino == inf[1].stat.st_ino
  479.       && inf[0].stat.st_dev == inf[1].stat.st_dev
  480.       && stat_result[0] == 0
  481.       && stat_result[1] == 0)
  482.     {
  483.       val = 0;
  484.       goto done;
  485.     }
  486.  
  487.   if (name0 == 0)
  488.     inf[0].dir_p = inf[1].dir_p;
  489.   if (name1 == 0)
  490.     inf[1].dir_p = inf[0].dir_p;
  491.  
  492.   /* Open the files and record their descriptors.  */
  493.  
  494.   for (i = 0; i <= 1; i++)
  495.     {
  496.       if (inf[i].desc == -1)
  497.     ;
  498.       else if (!strcmp (inf[i].name, "-"))
  499.     {
  500.       inf[i].desc = 0;
  501.       inf[i].name = "Standard Input";
  502.     }
  503.       /* Don't bother opening if stat already failed.  */
  504.       else if (stat_result[i] == 0 && ! inf[i].dir_p)
  505.     {
  506.       char *filename = inf[i].name;
  507.  
  508.       inf[i].desc = open (filename, O_RDONLY, 0);
  509.       if (0 > inf[i].desc)
  510.         {
  511.           perror_with_name (filename);
  512.           errorcount = 1;
  513.         }
  514.     }
  515.     }
  516.  
  517.   if (errorcount)
  518.     {
  519.  
  520.       /* If either file should exist but fails to be opened, return 2.  */
  521.  
  522.       val = 2;
  523.  
  524.     }
  525.   else if (inf[0].dir_p && inf[1].dir_p)
  526.     {
  527.       if (output_style == OUTPUT_IFDEF)
  528.     fatal ("-D option not supported with directories");
  529.  
  530.       /* If both are directories, compare the files in them.  */
  531.  
  532.       if (depth > 0 && !recursive)
  533.     {
  534.       /* But don't compare dir contents one level down
  535.          unless -r was specified.  */
  536.       message ("Common subdirectories: %s and %s\n",
  537.            inf[0].name, inf[1].name);
  538.       val = 0;
  539.     }
  540.       else
  541.     {
  542.       val = diff_dirs (inf[0].name, inf[1].name, 
  543.                compare_files, depth, 0, 0);
  544.     }
  545.  
  546.     }
  547.   else if (depth == 0 && (inf[0].dir_p || inf[1].dir_p))
  548.     {
  549.  
  550.       /* If only one is a directory, and it was specified in the command line,
  551.      use the file in that dir whose basename matches the other file.  */
  552.  
  553.       int dir_arg = (inf[0].dir_p ? 0 : 1);
  554.       int fnm_arg = (inf[0].dir_p ? 1 : 0);
  555.       char *p = rindex (inf[fnm_arg].name, '/');
  556.       char *filename = concat (inf[dir_arg].name,  "/",
  557.                    (p ? p+1 : inf[fnm_arg].name));
  558.  
  559.       inf[dir_arg].desc = open (filename, O_RDONLY, 0);
  560.  
  561.       if (0 > inf[dir_arg].desc)
  562.     {
  563.       perror_with_name (filename);
  564.       val = 2;
  565.     }
  566.       else
  567.     {
  568.       /* JF: patch from the net to check and make sure we can really free
  569.          this.  If it's from argv[], freeing it is a *really* bad idea */
  570.       if (0 != (dir_arg ? dir1 : dir0))
  571.         free (inf[dir_arg].name);
  572.       inf[dir_arg].name = filename;
  573.       if (fstat (inf[dir_arg].desc, &inf[dir_arg].stat) < 0)
  574.         pfatal_with_name (inf[dir_arg].name);
  575.  
  576.       inf[dir_arg].dir_p
  577.         = (S_IFDIR == (inf[dir_arg].stat.st_mode & S_IFMT));
  578.       if (inf[dir_arg].dir_p)
  579.         {
  580.           error ("%s is a directory but %s is not",
  581.              inf[dir_arg].name, inf[fnm_arg].name);
  582.           val = 1;
  583.         }
  584.       else
  585.         val = diff_2_files (inf, depth);
  586.     }
  587.  
  588.     }
  589.   else if (depth > 0 && (inf[0].dir_p || inf[1].dir_p))
  590.     {
  591.       /* Perhaps we have a subdirectory that exists only in one directory.
  592.      If so, just print a message to that effect.  */
  593.  
  594.       if (inf[0].desc == -1 || inf[1].desc == -1)
  595.     {
  596.       if (entire_new_file_flag && recursive)
  597.         val = diff_dirs (inf[0].name, inf[1].name, compare_files, depth,
  598.                  inf[0].desc == -1, inf[1].desc == -1);
  599.       else
  600.         {
  601.           char *dir = (inf[0].desc == -1) ? dir1 : dir0;
  602.           message ("Only in %s: %s\n", dir, name0);
  603.           val = 1;
  604.         }
  605.     }
  606.       else
  607.     {
  608.       /* We have a subdirectory in one directory
  609.          and a file in the other.  */
  610.  
  611.       if (inf[0].dir_p)
  612.         message ("%s is a directory but %s is not\n",
  613.              inf[0].name, inf[1].name);
  614.       else
  615.         message ("%s is a directory but %s is not\n",
  616.              inf[1].name, inf[0].name);
  617.       /* This is a difference.  */
  618.       val = 1;
  619.     }
  620.     }
  621.   else
  622.     {
  623.  
  624.       /* Both exist and both are ordinary files.  */
  625.  
  626.       val = diff_2_files (inf, depth);
  627.  
  628.     }
  629.  
  630.   /* Now the comparison has been done, if no error prevented it,
  631.      and VAL is the value this function will return.  */
  632.  
  633.   if (inf[0].desc > 0)
  634.     close (inf[0].desc);
  635.   if (inf[1].desc > 0)
  636.     close (inf[1].desc);
  637.  
  638.  done:
  639.   if (val == 0 && !inf[0].dir_p)
  640.     {
  641.       if (print_file_same_flag)
  642.     message ("Files %s and %s are identical\n",
  643.          inf[0].name, inf[1].name);
  644.     }
  645.   else
  646.     fflush (stdout);
  647.  
  648.   if (dir0 != 0)
  649.     free (inf[0].name);
  650.   if (dir1 != 0)
  651.     free (inf[1].name);
  652.  
  653.   return val;
  654. }
  655.